import {MapString, sdf} from "../public/sdf";
import {PromiseFunc} from "../PromiseFunc";
import socketIO = require('socket.io');
import * as io from "socket.io-client"
import {Log} from "../Log";

export class RpcClass {


    /**
     * 该进程关联的host主机名<主机名，是否是自身进程>,如果是自身进程的话可以省去socket传输过程
     * @type {Array}
     */
    static hostName: MapString<boolean> = {};


    /**
     * 原rpc业务实例,用于服务端函数调用
     */
    classInst: any = null;

    /**
     * rpc业务实例拷贝,用于客户端函数调用发起请求
     */
    clasRpc = {};

    /**
     * 记录所有已链接的rpc的socket<主机名，socket>
     */
    static sockets: MapString<SocketIOClient.Socket> = {}

    /**
     * 记录所有原对象实例<类名，原对象实例>
     */
    static instMap: MapString<MapString<Function>> = {}

    /**
     * 用于rpc调用时的临时目标host参数
     */
    httpServer = "";


    /**
     * 获取rpc服务链接
     * @returns {SocketIOClient.Socket}
     */
    newSocket() {
        let server = this.httpServer
        let sock = RpcClass.sockets[server];
        if (!sock) {
            sock = io.connect(server, {transports: ['websocket']});
            RpcClass.sockets[server] = sock;
            sock.on("_ret", (from: any[]) => {
                if (!from.length) {
                    Log.write(server + "非法服务：" + JSON.stringify(from))
                    sock.close();
                    delete  RpcClass.sockets[server];
                    return;
                }
                let ok = from[0] as number;
                let id = from[1] as string;
                let ret = from[2];
                let promi = RpcClass.rpcMap.get(id);
                if (promi) {
                    RpcClass.rpcMap.delete(id);
                    try {
                        if (ok) {
                            promi.resolve(ret);
                        } else {
                            promi.reject(Error(ret));
                        }
                    } catch (e) {
                        Log.write("rpc promise error：" + JSON.stringify(from), e)
                    }
                }
                else {
                    Log.write("rpc任务未找到：" + JSON.stringify(from))
                }

            });
            sock.on("error", err => {
                Log.write(server + " error：" + err)
            })

            sock.on("connect_error", err => {
                Log.write(server + " connect_error：" + err)
            })

            sock.on("connect_timeout", err => {
                Log.write(server + " connect_timeout：" + err)
            })

            sock.on("disconnect", err => {
                Log.write(server + " disconnect：" + err)
            })
        }
        return sock
    }

    private static rpcID = 0;
    /**
     * 唯一id与rpc响应函数
     * @type {Map<number, PromiseFunc>}
     */
    static rpcMap = new Map<string, PromiseFunc>();

    static getID(): string {
        return sdf.getUniqueId16();
    }

    /**
     * 客户端rpc请求函数
     * @param arr 请求参数
     * @returns {Promise<T>}
     */
    rpcFunc(arr: any[]): Promise<any> {
        let sock = this.newSocket();

        let promi = new Promise((reso, reject) => {
            let promiFunc: PromiseFunc = {
                resolve: reso,
                reject: reject,
                startTime: new Date(),
            }
            let id = RpcClass.getID();
            RpcClass.rpcMap.set(id, promiFunc);
            arr.push(id);
            sock.emit("_rpc", arr);
        })

        return promi;
    }

    /**
     *
     * @param rpcClass 业务rpc类名
     */
    constructor(private rpcClass: { new(): any }) {
        this.classInst = new rpcClass();
        for (let k in this.classInst) {
            this.clasRpc[k] = this.classInst[k];
        }
        let name = rpcClass.name;
        RpcClass.instMap[name] = this.classInst;

        let funcs = Object.getOwnPropertyNames(rpcClass.prototype)
        for (let f of funcs) {
            if (f === "constructor")
                continue;

            if (rpcClass.prototype[f] instanceof Function) {

                let this_ = this;
                this.clasRpc[f] = function rpcFn() {
                    var arr = Array<any>();
                    for (var i = 0; i < arguments.length; i++) {
                        arr.push(arguments[i]);
                    }

                    //对方主机是自己，直接调用
                    if (RpcClass.hostName[this_.httpServer]) {
                        return (this_.classInst[f] as Function).apply(this_.classInst, arr);
                    }


                    arr.push(name);
                    arr.push(f);
                    return this_.rpcFunc(arr);
                }
            }

        }
    }
}